package com.acxy.ants.core.config;

import com.acxy.ants.common.kit.StrKit;
import com.acxy.ants.core.annotation.Application;
import com.acxy.ants.core.annotation.Service;
import com.acxy.ants.core.kit.ScanKit;
import com.acxy.ants.resful.annotation.Controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

/**
 * @author MrShun
 * @version 1.0
 * @Date 2017-08-29
 */
public class Modules {

    private static final Logger logger = LoggerFactory.getLogger(Modules.class);

    private static List<Class<?>> serviceClassList = null, controllerClassList = null;


    private static List<String> xmlFileList = null, propFileList = null, resFileList = null;

    //jar模块里面的第一个Class文件 用来读取流
    private static ConcurrentMap<String, Class> firstClassMap = new ConcurrentHashMap<>();

    //jar模块里面的res文件列表
    private static ConcurrentMap<String, List<String>> resFileMap = new ConcurrentHashMap<>();

    //jar模块里面的controller class
    private static ConcurrentMap<String, List<Class<?>>> controllerClassMap = new ConcurrentHashMap<>();

    //jar模块里面的Xml Sql File
    private static ConcurrentMap<String, List> jarXmlFileMap = new ConcurrentHashMap<>();

    //jar模块里面的Properties File
    private static ConcurrentMap<String, List> jarPropFileMap = new ConcurrentHashMap<>();


    /**
     * 获取jar下面的注解类
     *
     * @param webINF webinf路径
     * @param jars   模块数组
     */
    public static void initModules(String webINF, String[] jars) {
        if (jars == null || jars.length == 0) return;
        for (String jarPath : jars) {
            logger.debug("初始化子模块[{}] ...", jarPath);
            JarFile jarFile = null;
            try {
                jarFile = new JarFile(webINF + File.separator + jarPath.trim());
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String strCls = entry.getName();
                    if (strCls.endsWith(".class")) {
                        strCls = strCls.substring(0, strCls.lastIndexOf(".")).replace("/", ".");
                        Class<?> cls = Class.forName(strCls);
                        firstClassMap.put(jarPath, cls);
                        if (cls != null && cls.getAnnotation(Controller.class) != null && !cls.isInterface()) {
                            if (controllerClassList == null) controllerClassList = new ArrayList<>();
                            controllerClassList.add(cls);
                        } else if (cls != null && cls.getAnnotation(Service.class) != null && !cls.isInterface()) {
                            if (serviceClassList == null) serviceClassList = new ArrayList<>();
                            serviceClassList.add(cls);
                        }
                    } else if (strCls.endsWith(".xml")) {
                        if (xmlFileList == null) xmlFileList = new ArrayList<>();
                        xmlFileList.add(strCls);
                    } else if (strCls.endsWith(".properties") || strCls.endsWith(".txt")) {
                        if (propFileList == null) propFileList = new ArrayList<>();
                        propFileList.add(strCls);
                    }

                    if(strCls.indexOf(".")!=-1){
                        if (resFileList == null) resFileList = new ArrayList<>();
                        resFileList.add(strCls);
                    }
                }
                controllerClassMap.put(jarPath, controllerClassList);
                jarXmlFileMap.put(jarPath, xmlFileList);
                jarPropFileMap.put(jarPath, propFileList);
                resFileMap.put(jarPath, resFileList);
            } catch (IOException e) {
                throw new RuntimeException("没有找到 [" + jarPath + "] 子模块!", e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("Class.forName 加载类失败!", e);
            }
        }
    }

    /**
     * 获取模块里面controller list
     *
     * @return
     */
    public static List<Class<?>> getModuleControllerList() {
        return controllerClassList;
    }

    /**
     * 获取模块里面controller Map
     *
     * @return
     */
    public static Map<String, List<Class<?>>> getModuleControllerMap() {
        return controllerClassMap;
    }

    /**
     * 获取模块里面service list
     *
     * @return
     */
    public static List<Class<?>> getModuleServiceList() {
        return serviceClassList;
    }

    /**
     * 获取模块里面的文件
     *
     * @param jar 模块名称
     * @return
     */
    public static List<InputStream> getModuleFileInputStream(String jar, int type) {
        List<InputStream> ins = new ArrayList<>();
        Class cls = firstClassMap.get(jar);
        List<String> strLists = null;
        switch (type){
            case 0: //读取xml文件
                strLists = jarXmlFileMap.get(jar);
                break;
            case 1://读取配置文件
                strLists = jarPropFileMap.get(jar);
                break;
        }
        if(strLists!=null) {
            for (String xml : strLists) {
                InputStream resourceAsStream = cls.getResourceAsStream("/" + xml);
                ins.add(resourceAsStream);
                logger.debug("读取[{}]里面的 {} 成功 ...", jar, xml);
            }
        }
        return ins;
    }

    /**
     * 读取模块jar里面的资源文件
     * @param jar 模块名称
     * @param resName 资源名称
     * @return
     */
    public static InputStream getModuleResInputStream(String jar, String resName){
        List<String> resLists = resFileMap.get(jar);
        InputStream resourceAsStream = null;
        if(resLists !=null && resLists.size()!=0) {
            int index = resLists.indexOf(resName);
            if(index != -1) {
                Class cls = firstClassMap.get(jar);
                resourceAsStream = cls.getResourceAsStream("/" + resLists.get(index));
            }
        }
        return resourceAsStream;
    }


    //全局Appication注解, 在jar模式下会用到
    private static Application application = null;

    private static String mainClass = null;

    /**
     * jar方式启动
     * @param jarPath
     */
    public static void initModules(String jarPath, Class<? extends Annotation> annotation, List<Class<?>> list) {
        if (StrKit.isBlank(jarPath)) return;
        JarFile jarFile;
        String clsName = null;
        try {
            jarFile = new JarFile(jarPath);
            if(application == null) {
                Manifest manifest = jarFile.getManifest();
                Attributes attributes = manifest.getMainAttributes();
                String applicationClass = attributes.getValue("Main-Class");
                int lastNum = applicationClass.lastIndexOf(".");
                if(lastNum == -1) throw new RuntimeException(applicationClass + " 运行主类必须带包名!");
                mainClass = applicationClass.substring(0, lastNum).replace(".", "/");
                application = Class.forName(applicationClass).getAnnotation(Application.class);
            }
            Enumeration<JarEntry> entries = jarFile.entries();
            String[] scanStr = application.scan();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String strCls = entry.getName();
                boolean check;
                if(scanStr != null && scanStr.length > 0)
                    check = existStr(scanStr, strCls);
                else
                    check = strCls.indexOf(mainClass)!=-1 ? true : false;
                if (strCls.endsWith(".class") && check) {
                    clsName = strCls.substring(0, strCls.lastIndexOf(".")).replace("/", ".");
                    Class<?> cls = Class.forName(clsName);
                    Annotation anno = cls.getAnnotation(annotation);
                    if(anno != null) list.add(cls);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("没有找到 [" + jarPath + "] !", e);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断字符串数组里面是否存在某个字符串
     * @param sts 字符串素组
     * @param s 判断字符
     * @return
     */
    private static boolean existStr(String[] sts, String s){
        for(String st: sts){
            st = st.replace(".", "/");
            if(s.indexOf(st) != -1) {
                return true;
            }
        }
        return false;
    }
}
